package Q7_06_Jigsaw;
import java.util.LinkedList;
public class Puzzle {
private LinkedList<Piece> pieces; /* Remaining pieces left to put away. */
private Piece[][] solution;
private int size;
public Puzzle(int size, LinkedList<Piece> pieces) {
this.pieces = pieces;
this.size = size;
}
/* Group pieces into border pieces (including corners) and inside pieces. */
public void groupPieces(LinkedList<Piece> cornerPieces, LinkedList<Piece> borderPieces, LinkedList<Piece> insidePieces) {
for (Piece p : pieces) {
if (p.isCorner()) {
cornerPieces.add(p);
} else if (p.isBorder()) {
borderPieces.add(p);
} else {
insidePieces.add(p);
}
}
}
/* Orient this corner piece so that its flat edges are on the top and left. */
public void orientTopLeftCorner(Piece piece) {
if (!piece.isCorner()) return;
Orientation[] orientations = Orientation.values();
for (int i = 0; i < orientations.length; i++) {
Edge current = piece.getEdgeWithOrientation(orientations[i]);
Edge next = piece.getEdgeWithOrientation(orientations[(i + 1) % orientations.length]);
if (current.getShape() == Shape.FLAT && next.getShape() == Shape.FLAT) {
piece.setEdgeAsOrientation(current, Orientation.LEFT);
return;
}
}
}
/* Bounds check. Check if this index is on a border (0 or size - 1) */
public boolean isBorderIndex(int location) {
return location == 0 || location == size - 1;
}
/* Given a list of pieces, check if any have an edge that matches this piece. Return the edge*/
private Edge getMatchingEdge(Edge targetEdge, LinkedList<Piece> pieces) {
for (Piece piece : pieces) {
Edge matchingEdge = piece.getMatchingEdge(targetEdge);
if (matchingEdge != null) {
return matchingEdge;
}
}
return null;
}
/* Put the edge/piece into the solution, turn it appropriately, and remove from list. */
private void setEdgeInSolution(LinkedList<Piece> pieces, Edge edge, int row, int column, Orientation orientation) {
Piece piece = edge.getParentPiece();
piece.setEdgeAsOrientation(edge, orientation);
pieces.remove(piece);
solution[row][column] = piece;
}
/* Return the list where a piece with this index would be found. */
private LinkedList<Piece> getPieceListToSearch(LinkedList<Piece> cornerPieces, LinkedList<Piece> borderPieces, LinkedList<Piece> insidePieces, int row, int column) {
if (isBorderIndex(row) && isBorderIndex(column)) {
return cornerPieces;
} else if (isBorderIndex(row) || isBorderIndex(column)) {
return borderPieces;
} else {
return insidePieces;
}
}
/* Find the matching piece within piecesToSearch and insert it at row, column. */
private boolean fitNextEdge(LinkedList<Piece> piecesToSearch, int row, int column) {
if (row == 0 && column == 0) {
Piece p = piecesToSearch.remove();
orientTopLeftCorner(p);
solution[0][0] = p;
} else {
/* Get the right edge and list to match. */
Piece pieceToMatch = column == 0 ? solution[row - 1][0] : solution[row][column - 1];
Orientation orientationToMatch = column == 0 ? Orientation.BOTTOM : Orientation.RIGHT;
Edge edgeToMatch = pieceToMatch.getEdgeWithOrientation(orientationToMatch);
/* Get matching edge. */
Edge edge = getMatchingEdge(edgeToMatch, piecesToSearch);
if (edge == null) return false; // Can't solve
Orientation orientation = orientationToMatch.getOpposite();
setEdgeInSolution(piecesToSearch, edge, row, column, orientation);
}
return true;
}
public boolean solve() {
/* Group pieces. */
LinkedList<Piece> cornerPieces = new LinkedList<Piece>();
LinkedList<Piece> borderPieces = new LinkedList<Piece>();
LinkedList<Piece> insidePieces = new LinkedList<Piece>();
groupPieces(cornerPieces, borderPieces, insidePieces);
/* Walk through puzzle, finding the piece that joins the previous one. */
solution = new Piece[size][size];
for (int row = 0; row < size; row++) {
for (int column = 0; column < size; column++) {
LinkedList<Piece> piecesToSearch = getPieceListToSearch(cornerPieces, borderPieces, insidePieces, row, column);
if (!fitNextEdge(piecesToSearch, row, column)) {
return false;
}
}
}
return true;
}
public Piece[][] getCurrentSolution() {
return solution;
}
}